﻿using SharpDX;
using System;
using System.IO;

namespace Engine.FileFormats3D.Tracks
{
    public class Route
    {
        #region Public Fields

        public float CurveRad;

        public float Delta;

        public float Ideal, Delta2;
        public short Margin1, Margin2, Tunnel, Column;

        #endregion Public Fields

        #region Private Fields

        private Matrix matrix;

        private Vector3 position;
        private byte v1, v2, v3, v4;

        #endregion Private Fields

        #region Public Constructors

        public Route(Vector3 rightEdge, Vector3 leftEdge, Route previewRoute)
            : this()
        {
            float length;

            this.position = new Vector3()
            {
                X = (rightEdge.X + leftEdge.X) / 2.0f,
                Y = (rightEdge.Y + leftEdge.Y) / 2.0f,
                Z = (rightEdge.Z + leftEdge.Z) / 2.0f
            };

            float dx;
            float dy;
            float dz;
            if (previewRoute != null)
            {
                dx = this.position.X - previewRoute.position.X;
                dy = this.position.Y - previewRoute.position.Y;
                dz = this.position.Z - previewRoute.position.Z;

                length = (float)Math.Sqrt(dx * dx + dy * dy + dz * dz);
                this.Delta = previewRoute.Delta + length;
            }

            dx = rightEdge.X - leftEdge.X;
            dz = rightEdge.Z - leftEdge.Z;

            length = (float)(Math.Sqrt(dx * dx + dz * dz) * 10);

            this.Margin1 = (short)(Math.Min(Math.Max(-length, short.MinValue), short.MaxValue) / 2.0);
            this.Margin2 = (short)(Math.Min(Math.Max(length, short.MinValue), short.MaxValue) / 2.0);
            this.Tunnel = 0;
            this.Column = 0;
            this.v1 = 0;
            this.v2 = 0;
            this.v3 = 0;
            this.v4 = 0;
        }

        public Route()
        {
            this.matrix = new Matrix();
        }

        #endregion Public Constructors

        #region Public Properties

        public Matrix Matrix
        {
            get
            {
                return this.matrix;
            }
            set
            {
                this.matrix = value;
            }
        }

        public Vector3 Position
        {
            get { return position; }
            set { position = value; }
        }

        #endregion Public Properties

        #region Public Methods

        public override string ToString()
        {
            float angle = (float)GeoMath.ConvertRadianToDegree(Math.Atan2(matrix.M11, matrix.M13));
            return string.Format("XYZ: {0}:{1}:{2} angle: {3}° ideal: {4} Margin1/2: {5}:{6} T:{7} C:{8} D1/2: {9}/{10}", this.Position.X, this.Position.Y, this.Position.Z, angle, this.Ideal, this.Margin1, this.Margin2, this.Tunnel, this.Column, this.Delta, this.Delta2);
        }

        #endregion Public Methods

        #region Internal Methods

        internal static Route FromStream(BinaryReader br)
        {
            return new Route()
            {
                position = new Vector3()
                {
                    X = br.ReadSingle(),
                    Y = br.ReadSingle(),
                    Z = br.ReadSingle(),
                },
                Delta = br.ReadSingle(),
                CurveRad = br.ReadSingle(),
                matrix = new Matrix()
                {
                    M11 = br.ReadSingle(),
                    M12 = br.ReadSingle(),
                    M13 = br.ReadSingle(),

                    M21 = br.ReadSingle(),
                    M22 = br.ReadSingle(),
                    M23 = br.ReadSingle(),

                    M31 = br.ReadSingle(),
                    M32 = br.ReadSingle(),
                    M33 = br.ReadSingle()
                },
                Ideal = br.ReadSingle(),
                Delta2 = br.ReadSingle(),
                Margin1 = br.ReadInt16(),
                Margin2 = br.ReadInt16(),
                Tunnel = br.ReadInt16(),
                Column = br.ReadInt16(),
                v1 = br.ReadByte(),
                v2 = br.ReadByte(),
                v3 = br.ReadByte(),
                v4 = br.ReadByte()
            };
        }

        internal void ToStream(BinaryWriter bw)
        {
            bw.Write((Single)this.position.X);
            bw.Write((Single)this.position.Y);
            bw.Write((Single)this.position.Z);
            bw.Write((Single)Delta);
            bw.Write((Single)CurveRad);

            bw.Write((Single)matrix.M11);
            bw.Write((Single)matrix.M12);
            bw.Write((Single)matrix.M13);

            bw.Write((Single)matrix.M21);
            bw.Write((Single)matrix.M22);
            bw.Write((Single)matrix.M23);

            bw.Write((Single)matrix.M31);
            bw.Write((Single)matrix.M32);
            bw.Write((Single)matrix.M33);

            bw.Write((Single)Ideal);
            bw.Write((Single)Delta2);
            bw.Write((short)Margin1);
            bw.Write((short)Margin2);
            bw.Write((short)Tunnel);
            bw.Write((short)Column);
            bw.Write((byte)v1);
            bw.Write((byte)v2);
            bw.Write((byte)v3);
            bw.Write((byte)v4);
        }

        #endregion Internal Methods
    }
}